 /*******************************************************************************
  * Copyright (c) 2004, 2006 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
  * http://www.eclipse.org/legal/epl-v10.html
  *
  * Contributors:
  * IBM Corporation - initial API and implementation
  *******************************************************************************/

 package org.eclipse.ui.internal.intro.impl.model;

 import org.eclipse.core.runtime.IConfigurationElement;
 import org.eclipse.ui.internal.intro.impl.model.util.BundleUtil;
 import org.eclipse.ui.internal.intro.impl.util.StringUtil;
 import org.osgi.framework.Bundle;
 import org.w3c.dom.Element ;

 /**
  * An intro config component. All config components can get to their defining
  * config element or bundle depending from where the element was loaded.
  * <p>
  * Class Rules:
  * <ul>
  * <li>If an element does not appear as a child under any node, then that
  * element does not need a type to be defined.</li>
  * <li>Each subclass must ensure that it properly supports cloning. This means
  * that if a deep copy is needed, the subclass must override the base behavior
  * here.</li>
  * <li>if cloning is not needed, override clone method and throw an unsupported
  * cloning exception. For now, only pages and targets of includes are cloneable.
  * </li>
  * </ul>
  * <p>
  * Note: This is an abstract base class for all classes in the Intro Model. <br>
  * Clients are not expected to implement or subclass this class, or any of its
  * subclasses.
  */
 public abstract class AbstractIntroElement implements Cloneable {

     /**
      * Type constant which identifies an IntroModelRoot element.
      */
     public static final int MODEL_ROOT = 1;

     /**
      * Type constant which identifies an IntroPartPresentation element.
      */
     public static final int PRESENTATION = 1 << 1;

     /**
      * Type constant which identifies an IntroHomePage element.
      */
     public static final int HOME_PAGE = 1 << 2;

     /**
      * Type constant which identifies the IntroPage element.
      */
     public static final int PAGE = 1 << 3;

     /**
      * Type constant which identifies the AbstractIntroPage element.
      */
     public static final int ABSTRACT_PAGE = HOME_PAGE | PAGE;

     /**
      * Type constant which identifies an IntroDiv element.
      */
     public static final int GROUP = 1 << 4;

     /**
      * Type constant which identifies the AbstractIntroContainer element.
      */
     public static final int ABSTRACT_CONTAINER = ABSTRACT_PAGE | GROUP
             | MODEL_ROOT;

     /**
      * Type constant which identifies the IntroHtml element.
      */
     public static final int HTML = 1 << 5;

     /**
      * Type constant which identifies the IntroLink element.
      */
     public static final int LINK = 1 << 6;

     /**
      * Type constant which identifies the IntroImage element.
      */
     public static final int IMAGE = 1 << 7;

     /**
      * Type constant which identifies the IntroInclude element.
      */
     public static final int INCLUDE = 1 << 8;

     /**
      * Type constant which identifies the IntroText element.
      */
     public static final int TEXT = 1 << 9;

     /**
      * Type constant which identifies the IntroContainerExtension element.
      */
     public static final int CONTAINER_EXTENSION = 1 << 10;

     /**
      * Type constant which identifies the IntroHead element.
      */
     public static final int HEAD = 1 << 11;

     /**
      * Type constant which identifies the IntroHead element.
      */
     public static final int PAGE_TITLE = 1 << 12;

     /**
      * Type constant which identifies the IntroAnchor element.
      */
     public static final int ANCHOR = 1 << 13;

     /**
      * Type constant which identifies the IntroContentProvider element.
      */
     public static final int CONTENT_PROVIDER = 1 << 14;

     /**
      * Type constant which identifies the LaunchBarElement.
      */
     public static final int LAUNCH_BAR = 1 << 15;

     /**
      * Type constant which identifies the launch bar shortcut.
      */
     public static final int LAUNCH_BAR_SHORTCUT = 1 << 16;

     /**
      * Type constant which identifies am injected IFrame model element.
      */
     public static final int INJECTED_IFRAME = 1 << 17;
     
     /**
      * Type constant for the theme element.
      */
     public static final int THEME = 1 << 18;
     
     /**
      * Type constant for the hr element.
      */
     public static final int HR = 1 << 19;


     /**
      * Type constant which identifies the AbstractText element.
      */
     public static final int ABSTRACT_TEXT = HTML | LINK | CONTENT_PROVIDER;

     /**
      * Type constant which identifies the AbstractCommonIntroElement element.
      */
     public static final int BASE_ELEMENT = ABSTRACT_CONTAINER | ABSTRACT_TEXT
             | IMAGE | TEXT | PAGE_TITLE;

     /**
      * Type constant which identifies any element in the Intro Model which can
      * have an id. Note: eventhough IntroStandbyContentPart has an id, it does
      * not appear as a child in the model, and so it does not have a type.
      */
     public static final int ID_ELEMENT = BASE_ELEMENT | ANCHOR;

     /**
      * Type constant which identifies any element in the Intro Model.
      */
     public static final int ELEMENT = ID_ELEMENT | CONTAINER_EXTENSION | HEAD
             | INCLUDE | PRESENTATION | LAUNCH_BAR | LAUNCH_BAR_SHORTCUT;



     private AbstractIntroElement parent;
     private Object cfgElement;
     private Bundle bundle;
     private String mixinStyle;


     /**
      * Constructor used when model elements are being loaded from plugin.xml.
      */
     AbstractIntroElement(IConfigurationElement element) {
         cfgElement = element;
         bundle = BundleUtil.getBundleFromConfigurationElement(element);
     }


     /**
      * Constructor used when model elements are being loaded from an xml content
      * file. Bundle is propagated down the model to enable resolving resources
      * relative to the base of the bundle.
      *
      * @param element
      * @param pd
      */
     AbstractIntroElement(Element element, Bundle bundle) {
         this.cfgElement = element;
         this.bundle = bundle;
     }


     /**
      * Constructor used when model elements are being loaded from an xml content
      * file. Bundle AND base is propagated down the model to enable resolving
      * resources relative to the xml content file. The base is set to point to
      * the relative location of the parent folder that holds the content file.
      * In the case of a configExtension, it is set to point to the relative
      * position of the parent folder that holds the extension. Only when needed,
      * the base field is stored in a model element. This saves memory.
      *
      * @param element
      * @param pd
      */
     AbstractIntroElement(Element element, Bundle bundle, String base) {
         this(element, bundle);
     }




     /**
      * Returns the configuration element from which this intro element was
      * loaded. In the case of extension, returns the configuration element of
      * the defining extension.
      *
      * @return
      */
     public IConfigurationElement getCfgElement() {
         return cfgElement instanceof IConfigurationElement?(IConfigurationElement)cfgElement:null;
     }
     
     public Element getElement() {
         return cfgElement instanceof Element?(Element)cfgElement:null;
     }

     /**
      * DOM getAttribute retruns an empty string (not null) if attribute is not
      * defined. Override this behavior to be consistent with Intro Model, and
      * IConfiguration element.
      *
      * @param element
      * @param att
      * @return
      */
     protected String getAttribute(Element element, String att) {
         if (element.hasAttribute(att)) {
             String value = element.getAttribute(att);
             if (value!=null) {
                 IntroModelRoot root = getModelRoot();
                 if (root!=null)
                     return root.resolveVariables(value);
                 return value;
             }
         }
         return null;
     }

     /**
      * Util method to parse a comma separated list of values
      *
      * @param element
      * @param att
      * @return
      */
     protected String [] getAttributeList(Element element, String att) {
         if (element.hasAttribute(att)) {
             String value = element.getAttribute(att);
             if (value!=null) {
                 IntroModelRoot root = getModelRoot();
                 if (root!=null)
                     value = root.resolveVariables(value);
                 return StringUtil.split(value, ","); //$NON-NLS-1$
 }
         }
         /*
         if (element.hasAttribute(att))
             return element.getAttribute(att).split(","); //$NON-NLS-1$
             */
         return null;
     }
     
     protected void loadFromParent() {
     }


     /**
      * Returns the plugin descriptor of the plugin from which this intro element
      * was loaded. In the case of extension, returns the plugin descriptor of
      * the plugin defining the extension.
      *
      * @return
      */
     public Bundle getBundle() {
         return bundle;
     }



     /**
      * Returns the specific model type of this intro element. To be implemented
      * by all subclasses.
      *
      * @return returns one of the model class types defined in this class.
      */
     public abstract int getType();


     /**
      * Returns the parent of this intro element.
      * <p>
      * Rules:
      * <ul>
      * <li>For the model root, it retruns null.</li>
      * <li>For the introPart presentation it returns a model root.</li>
      * <li>For Pages, it returns an intro model root.</li>
      * <li>For all other elements, it retruns a subclass of abstract container.
      * </li>
      * <li>for divs that are children of configs (shared divs), it returns the
      * holding model root.</li>
      * <li>for Head elements that are children of Implementation elements
      * (shared Heads), it returns the holding presentation element.</li>
      * </ul>
      *
      * @return returns the parent of this intro element. Null only for model
      * root.
      */
     public AbstractIntroElement getParent() {
         return parent;
     }

     /**
      * @param parent
      * The parent to set.
      */
     public void setParent(AbstractIntroElement parent) {
         this.parent = parent;
         if (parent!=null)
             loadFromParent();
     }

     public void setBundle(Bundle bundle) {
         this.bundle = bundle;
     }

     /**
      * Returns the parent page holding this intro element. For the model root
      * and the introPart presentation it returns null. For Pages, it returns the
      * page itself. For all other element, returns the holding page.
      * <p>
      * Exceptions:
      * <ul>
      * <li>for divs that are children of configs (shared divs), it returns
      * null.</li>
      * <li>for Head elements that are children of Implementation elements
      * (shared Heads), it returns null.</li>
      * </ul>
      */
     public AbstractIntroPage getParentPage() {
         // return yourself if you are a page.
 if (isOfType(AbstractIntroElement.ABSTRACT_PAGE))
             return (AbstractIntroPage) this;

         AbstractIntroElement parent = getParent();
         if (parent == null)
             return null;

         while (parent != null && parent.getParent() != null
                 && !parent.isOfType(AbstractIntroElement.ABSTRACT_PAGE))
             parent = parent.getParent();
         if (parent.isOfType(ABSTRACT_PAGE))
             return (AbstractIntroPage) parent;
         return null;
     }
     
     public IntroModelRoot getModelRoot() {
         // return yourself if you are a model root.
 if (isOfType(AbstractIntroElement.MODEL_ROOT))
             return (IntroModelRoot) this;

         AbstractIntroElement parent = getParent();
         if (parent == null)
             return null;

         while (parent != null && parent.getParent() != null
                 && !parent.isOfType(AbstractIntroElement.MODEL_ROOT))
             parent = parent.getParent();
         if (parent.isOfType(MODEL_ROOT))
             return (IntroModelRoot) parent;
         return null;
     }


     /**
      * Returns whether the element is among the specified element types. An
      * example of an element mask is as follows:
      * <p>
      * <code>
      * int elementMask = IntroElement.ABSTRACT_CONTAINER;
      * int elementMask = IntroElement.DIV | IntroElement.DEFAULT_LINK;
      * </code>
      *
      * @param elementMask
      * element mask formed by bitwise OR of element type constants
      * defined in this class.
      * @return <code>true</code> if this element has a matching type, and
      * <code>false</code> otherwise.
      */
     public boolean isOfType(int elementMask) {
         return (getType() & elementMask) != 0;
     }

     /**
      * Returns whether the types of all the elements in the given array are
      * among the specified element types. <br>
      * An example of an element mask is as follows:
      * <p>
      * <code>
      * int elementMask = IntroElement.DIV | IntroElement.DEFAULT_LINK;
      * </code>
      *
      * @return <code>true</code> if all elements are of the right type, and
      * <code>false</code> if the list is empty, or at least one
      * element is not of the specified types.
      */
     public static final boolean allElementsAreOfType(
             AbstractIntroElement[] elements, int elementMask) {
         // if we have an empty list, no point going on.
 if (elements.length == 0)
             return false;

         for (int i = 0; i < elements.length; i++) {
             AbstractIntroElement element = elements[i];
             if (!element.isOfType(elementMask))
                 return false;
         }
         return true;
     }

     /**
      * Shallow copy. The design of cloning this model assumes that when a
      * container is cloned, all its children must be cloned and reparented to
      * it, hence one clone of this container object. This is why we have a
      * shallow copy here.
      */
     public Object clone() throws CloneNotSupportedException {
         return super.clone();
     }


     
     public String getMixinStyle() {
         return mixinStyle;
     }


     
     public void setMixinStyle(String mixinStyle) {
         this.mixinStyle = mixinStyle;
     }



 }
